package com.heyi.saas.start.config;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.parser.SqlParserHelper;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.heyi.saas.start.service.ShareDataService;
import com.heyi.saas.start.service.TenantTables;
import lombok.SneakyThrows;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.springframework.beans.factory.annotation.Autowired;

import java.sql.Connection;
import java.util.List;
import java.util.Map;

/**
 * 重写 TenantLineInnerInterceptor
 * @author dengfeng
 * @date 2021/9/2214:20
 */
public class MyTenantLineInnerInterceptor extends TenantLineInnerInterceptor {

    @Autowired
    public TenantTables tenantTables;

    @Autowired
    ShareDataService shareDataService;

    public MyTenantLineInnerInterceptor(TenantLineHandler tenantLineHandler) {
        super(tenantLineHandler);
    }

    /** 功能描述
     　* @Description: 重写此方法来检查：共享数据非平台用户不能修改和删除
     　* @author dengfeng
     　* @date 2021/8/13 18:19
     　*/
    @SneakyThrows
    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
            // 对于在租户隔离中的表且有共享数据的，非平台用户不得操作共享数据
            if(checkSharedDataScope(sct, mpBs, getTenantLineHandler())){
                throw new Exception("非平台用户不得操作共享数据");
            }

            if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {
                return;
            }

            if (SqlParserHelper.getSqlParserInfo(ms)) {
                return;
            }

            mpBs.sql(this.parserMulti(mpBs.sql(), (Object)null));
        }

    }

    @Override
    protected void processPlainSelect(PlainSelect plainSelect) {
        FromItem fromItem = plainSelect.getFromItem();
        Expression where = plainSelect.getWhere();
        this.processWhereSubSelect(where);
        if (fromItem instanceof Table) {
            Table fromTable = (Table)fromItem;
            if (!getTenantLineHandler().ignoreTable(fromTable.getName())) {
                Expression expression = builderExpression(where, fromTable);

                plainSelect.setWhere(expression);
            }
        } else {
            this.processFromItem(fromItem);
        }

        List<Join> joins = plainSelect.getJoins();
        if (joins != null && joins.size() > 0) {
            joins.forEach((j) -> {
                this.processJoin(j);
                this.processFromItem(j.getRightItem());
            });
        }

    }

    @Override
    protected Expression builderExpression(Expression currentExpression, Table table) {
        EqualsTo equalsTo = new EqualsTo();
        equalsTo.setLeftExpression(this.getAliasColumn(table));
        equalsTo.setRightExpression(getTenantLineHandler().getTenantId());
        Expression tenantExpression = equalsTo;

        // dengfeng 增加共享数据
        int index = tenantTables.getTenantTableList().indexOf(table.getName());
        if(index >= 0 && tenantTables.getIncludeSharing(index)) {
            EqualsTo equalsToRight = new EqualsTo();
            equalsToRight.setLeftExpression(this.getAliasColumn(table));
            equalsToRight.setRightExpression(new LongValue(0));

            tenantExpression = new Parenthesis(new OrExpression(equalsTo, equalsToRight));
        }
        if (currentExpression == null) {
            return tenantExpression;
        } else {
            return currentExpression instanceof OrExpression ? new AndExpression(new Parenthesis(currentExpression), tenantExpression) : new AndExpression(currentExpression, tenantExpression);
        }
    }

    @Override
    protected Column getAliasColumn(Table table) {
        StringBuilder column = new StringBuilder();
        if (table.getAlias() != null) {
            column.append(table.getAlias().getName()).append(".");
        } else {
            column.append(table.getName()).append(".");
        }

        column.append(getTenantLineHandler().getTenantIdColumn());
        return new Column(column.toString());
    }


    private boolean checkSharedDataScope(SqlCommandType sct, PluginUtils.MPBoundSql mpBs, TenantLineHandler tenantLineHandler) throws Exception {
        //插入操作不需要检查
        if(sct == SqlCommandType.INSERT) {
            return false;
        }
        String tableName = null;
        if (sct == SqlCommandType.UPDATE) {
            tableName = ((Update) CCJSqlParserUtil.parse(mpBs.sql())).getTable().getName();
        } else {
            tableName = ((Delete) CCJSqlParserUtil.parse(mpBs.sql())).getTable().getName();
        }
        // 对做租户隔离的有共享数据的表进行检查
        if (!tenantLineHandler.ignoreTable(tableName)) {
            int index = tenantTables.getTenantTableList().indexOf(tableName);
            if (index >= 0 && tenantTables.getIncludeSharing(index)) {
                Object idValue = null;
                //找到id的字段，得到值 ，和缓存中的共享数据比较,有则拒绝执行
                if (mpBs.parameterObject() instanceof String) {
                    if (mpBs.parameterMappings().stream().anyMatch(o -> "id".equals(o.getProperty()))) {
                        idValue = mpBs.parameterObject();
                    }
                }
                if (mpBs.parameterObject() instanceof MapperMethod.ParamMap) {
                    MapperMethod.ParamMap<Object> paramMap = (MapperMethod.ParamMap) mpBs.parameterObject();

                    for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
                        if (entry.getValue() instanceof LambdaUpdateWrapper) {
                            LambdaUpdateWrapper updateWrapper = (LambdaUpdateWrapper) entry.getValue();
                            String sqlSegment = updateWrapper.getExpression().getSqlSegment();
                            if (sqlSegment.contains("=")) {
                                String[] params = sqlSegment.replace("(", "").replace(")", "").split("=");
                                if ("id".equals(params[0].trim())) {
                                    idValue = updateWrapper.getParamNameValuePairs().get(params[1].substring(params[1].lastIndexOf(".") + 1).replace("}", ""));
                                    break;
                                }
                            }
                        }
                    }
                }

                if (idValue != null && shareDataService.getIdList(tableName).contains(idValue)) {
                    return true;
                }
            }
        }
        return false;
    }

}
